package dcutils;

// Import Java JDK Classes
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * A class which retains reference to the last N number of events E within an internal history.<br/>
 * The internal history is a LinkedList.<br/>
 * T will commonly be type String.<br/>
 * E.g.: A History<String> defined with parameter 50, will keep the 50 latest strings sent to it.<br/>
 * E.g.: A History<Integer> defined with parameter 15, will keep the 15 latest integers sent to it.<br/>
 * @author dca
 */
public class History<E> {
   /** The maximum number of history events to retain.<br/> */
   private int depth;

   /** The list to keep history events.<br/> */
   private LinkedList<E> history;

   /**
    * Creates a new instance of this class, which will only remember the <code>depth</code> most recent number of events.<br/>
    * @param depth The number of most recent events to remember.<br/>
    */
   public History(int depth) {
      this.depth = depth;
      history = new LinkedList<>();
   } // END constructor

   /**
    * Returns <code>depth</code>.<br/>
    * @return <code>depth</code>.<br/>
    */
   public int depth() {
      return this.depth;
   } // END depth

   /**
    * Returns the number of events in the history/the history size.<br/>
    * @return history.size().<br/>
    */
   public int numEvents() {
      return history.size();
   } // END numEvents

   /**
    * Adds an <code>event</code> to the internal history.<br/>
    * @param event A new event.<br/>
    * @return <code>this</code> A reference to this history instance.<br/>
    */
   public History<E> add(E event) {
      history.addFirst(event);
      if(history.size() > depth) history.removeLast();
      return this;
   } // END add

   /**
    * Gets an event from the internal history.<br/>
    * Age is specified zero-based.<br/>
    * If there are not enough events in the history to get by <code>age</code>, then <i>null</i> will be returned.<br/>
    * @param age Determines how far back the event occurred, zero-based.  I.e. if <code>age</code> is 2, then the 3rd most recent event instance will be returned.<br/>
    * @return <code>E</code> The event instance.<br/>
    */
   public E getEvent(int age) {
      Preconditions p = new Preconditions();
      p.checkArgument(age >= 0, String.format("Age: %d cannot be negative", age));
      p.checkArgument(age <= depth, String.format("Age: %d is greater than depth: %d", age, depth));
      p.reportViolations();

      return age >= history.size() ? null : history.get(age);
   } // END getEvent

   /**
    * Returns the most recent event/the zeroth event.<br/>
    * If the internal history does not yet contain events, then <i>null</i> will be returned.<br/> 
    * @return <code>E</code> The event instance.<br/>
    */
   public E getMostRecentEvent() {
      return getEvent(0);
   } // ENd getMostRecentEvent

   /**
    * Returns a list of the <code>num</code> most recent events.<br/>
    * If there are not enough events in the history to get by <code>num</code>, then <i>null</i> will be returned.<br/>
    * @param num The number of events to return.<br/>
    * @return <code>List</code> A collection of recent events.<br/>
    */
   public List<E> getMostRecentEvents(int num) {
      List<E> events = new ArrayList<>();
      for(int index = 0 ; index <= num ; ++index) {
         events.add(getEvent(index));
      } // END loop
      return events;
   } // END getMostRecentEvents

   /**
    * Returns the oldest event from the internal history.<br/>
    * @return <code>E</code> The event instance.<br/>
    */
   public E getOldestEvent() {
      return history.getLast();
   } // END getOldestEvent

   /**
    * Returns true or false whether the internal history is empty.<br/>
    * @return True if the internal history is empty; False otherwise.<br/>
    */
   public boolean isEmpty() {
      return history.isEmpty();
   } // END isEmpty

   /**
    * Returns a string representation of the internal history.<br/>
    * @return <code>history.toString()</code>.<br/>
    */
   @Override
   public String toString() {
      return history.toString();
   } // END toString
} // END class History
